module fn.adt.Tree

open System



type Tree<'a> =
    | Node of 'a * Tree<'a> list
    
    member this.root : 'a =
        match this with
        | Node (r, _) -> r
    member this.subtrees : Tree<'a> list =
        match this with
        | Node (_, sb) -> sb
    
    member this.descendants = this :: [for s in this.subtrees do yield! s.descendants]
    member this.nodes = this.root :: [for s in this.subtrees do yield! s.nodes]
    
    member this.apply_to_values (fn: 'a -> 'b) =
        Node(fn (this.root), [for s in this.subtrees -> s.apply_to_values fn])
        
    override this.ToString() = 
        match this with
        | Node(r, []) -> r.ToString()
        | Node(r, sb) -> sprintf "%O{%s}" r ([|for s in sb -> s.ToString()|] |> String.concat ",")

    member this.split (delimiter : 'a) =
        match this with
        | Node(r, sb) when Unchecked.equals r delimiter -> [for t in sb do yield! t.split delimiter]
        | _ -> [this]